package org.mspring.mlog;

import org.mspring.mlog.dao.ArticleDao;
import org.mspring.nbee.common.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;

/**
 * @author Gao Youbo
 * @since 2015-01-20 13:07
 */
@Service
public class ArticleViewCountService extends TimerTask {

    private final List<ViewCountItem> data = new ArrayList<>();
    private final int MAX_ITEM_COUNT = 100000;

    private ArticleDao articleDao;

    /**
     * 点击量+1
     *
     * @param articleId
     * @return
     */
    public int increment(long articleId) {
        ViewCountItem item = getViewCountItem(articleId);
        if (item == null || item.getViewCount() < 0) {
            return 0;
        }
        item = item.increment(articleId);
        return item.getViewCount();
    }

    /**
     * 获取点击量
     *
     * @param articleId
     * @return
     */
    public int getViewCount(long articleId) {
        ViewCountItem item = getViewCountItem(articleId);
        if (item == null || item.getViewCount() < 0) {
            return 0;
        }
        return item.getViewCount();
    }

    /**
     * 持久化数据
     */
    @Transactional
    public void saveViewCount() {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        for (ViewCountItem item : data) {
            if (!item.isChanged() || item.getViewCount() <= 0) {
                continue;
            }
            boolean flag = articleDao.updateArticleViewCount(item.getArticleId(), item.getViewCount());
            if (flag) {
                item.setChanged(false);
            }
        }
    }

    private ViewCountItem getViewCountItem(long articleId) {
        if (articleId < 1L) {
            return null;
        }
        ViewCountItem item = getCache(articleId);
        if (item == null) {
            int viewCount = articleDao.getArticleViewCount(articleId);
            item = ViewCountItem.newInstance(articleId, viewCount);
            putCache(item);
        }
        return item;
    }

    /**
     * 设置缓存
     *
     * @param item
     */
    private void putCache(ViewCountItem item) {
        if (item == null || item.getArticleId() < 1L) {
            return;
        }
        if (data.size() > MAX_ITEM_COUNT) {
            data.remove(0);
        }
        data.add(item);
    }

    /**
     * 获取缓存
     *
     * @param articleId
     * @return
     */
    private ViewCountItem getCache(long articleId) {
        if (CollectionUtils.isEmpty(data)) {
            return null;
        }
        Collections.sort(data, new ViewCountItemComparator());
        int index = Collections.binarySearch(data, ViewCountItem.newInstance(articleId, 0), new ViewCountItemComparator());
        if (index < 0) {
            return null;
        }
        return data.get(index);
    }

    @PostConstruct
    public void init() {
        Timer timer = new Timer("文章点击量同步线程");
        timer.schedule(this, 1000L * 60 * 1, 1000L * 60 * 1); //延迟一分钟，一分钟同步一次
    }

    @PreDestroy
    public void destory() {
        saveViewCount();
    }

    @Override
    public void run() {
        saveViewCount();
    }

    private static class ViewCountItem {
        private long articleId;
        private int viewCount;
        private boolean changed = false;
        private long changeTime;

        public static ViewCountItem newInstance(long articleId, int viewCount) {
            ViewCountItem item = new ViewCountItem();
            item.setArticleId(articleId);
            item.setViewCount(viewCount);
            item.setChanged(false);
            item.setChangeTime(System.currentTimeMillis());
            return item;
        }

        public ViewCountItem increment(long articleId) {
            if (this.viewCount != -1) { //等于-1说明文章不存在
                this.viewCount += 1;
                this.changed = true;
                this.changeTime = System.currentTimeMillis();
            }
            return this;
        }

        public long getArticleId() {
            return articleId;
        }

        public void setArticleId(long articleId) {
            this.articleId = articleId;
        }

        public int getViewCount() {
            return viewCount;
        }

        public void setViewCount(int viewCount) {
            this.viewCount = viewCount;
        }

        public boolean isChanged() {
            return changed;
        }

        public void setChanged(boolean changed) {
            this.changed = changed;
        }

        public long getChangeTime() {
            return changeTime;
        }

        public void setChangeTime(long changeTime) {
            this.changeTime = changeTime;
        }
    }


    /**
     * 排序对象
     */
    private static class ViewCountItemComparator implements Comparator<ViewCountItem> {
        @Override
        public int compare(ViewCountItem o1, ViewCountItem o2) {
            if (o1.getArticleId() > o2.getArticleId()) {
                return -1;
            } else if (o1.getArticleId() < o2.getArticleId()) {
                return 1;
            }
            return 0;
        }
    }

    @Autowired
    public void setArticleDao(ArticleDao articleDao) {
        this.articleDao = articleDao;
    }
}
